package com.magenta.maxoptra.geocoding.decarta;

import com.magenta.maxoptra.dto.GeoLocationRecord;
import com.magenta.maxoptra.gis.*;
import com.magenta.maxoptra.gis.commons.StringUtils;
import com.magenta.mrs.basegeocoding.BaseGeocoding;
import com.magenta.mrs.util.GeoInformationClient;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;

import java.util.ArrayList;
import java.util.List;

/**
 * Created const Date: 23.03.2009 Time: 19:28:40
 * <p/>
 * The Implementation of the Decarta geocoding/routing service
 * <p/>
 *
 * @author Konstantin Pestrikov
 * @see //com.magenta.mrs.scheduler.api.ssi.DecartaRequestDispatcherMBean
 */
public class GeocoderDecartaImpl extends BaseGeocoding<Float> implements IGeocoding, IGeoRouting {

    @Override
    public String getProviderName() {
        return MapProviderType.decarta.name();
    }

    @Override
    protected boolean accuracyIsAccurateEnough(String accuracyThreshold, Float accuracy) {
        Float accuracyThresholdParsed = Float.valueOf(accuracyThreshold);
        return accuracy >= accuracyThresholdParsed;
    }

    class DecartaParameters {
        private String serverUrl = null;
        private String mapId = null;
        private String geocodingKey = null;

        public DecartaParameters(String serverUrl, String mapId, String geocodingKey) {
            this.geocodingKey = geocodingKey;
            this.mapId = mapId;
            this.serverUrl = serverUrl;
        }

        public String getServerUrl() {
            return serverUrl;
        }

        public String getMapId() {
            return mapId;
        }

        public String getGeocodingKey() {
            return geocodingKey;
        }

    }


    private DecartaParameters getDecartaParameters() throws GeoException {
        GisParameters gisParams = getProviderParams();
        String serverUrl;
        String mapId;
        String geocodingKey;
        String providerName = getProviderName();
        if (gisParams != null) {
            serverUrl = (String) gisParams.get(GisParameter.base_url.withProvider(providerName));
            if (StringUtils.isEmpty(serverUrl)) {
                throw new GeoException("Parameter " + GisParameter.base_url + " is not set for " + providerName);
            }
            mapId = (String) gisParams.get(GisParameter.map_id.withProvider(providerName));
            if (StringUtils.isEmpty(mapId)) {
                throw new GeoException("Parameter " + GisParameter.map_id + " is not set for " + providerName);
            }
            geocodingKey = (String) gisParams.get(GisParameter.geocoding_key.withProvider(providerName));
            if (StringUtils.isEmpty(geocodingKey)) {
                throw new GeoException("Parameter " + GisParameter.geocoding_key + " is not set for " + providerName);
            }
        } else {
            throw new GeoException("Gis parameters is not set for " + providerName);
        }
        return new DecartaParameters(serverUrl, mapId, geocodingKey);
    }


    private boolean setProperties() throws GeoException {
        //DecartaGeocodingKey key = (DecartaGeocodingKey) getGeocodingKey(region, forRouting);
        // database stores "|"-separated login/passwod in the GeocodingKey key1 field

        DecartaParameters gisParams = getDecartaParameters();

        String geocodingKey = gisParams.getGeocodingKey();
        String mapId = gisParams.getMapId();
        String[] creds = geocodingKey.split("\\|");
        String login = creds[0];
        String pass = creds[1];

        System.setProperty("clientName", login);
        System.setProperty("clientPassword", pass);
        System.setProperty("decartaWSUrl", gisParams.getServerUrl());
        //System.setProperty("http.proxyHost", proxy.getHost());
        //System.setProperty("http.proxyPort", "" + proxy.getPort());
        System.setProperty("serviceConfiguration", mapId);
        //Authenticator.setDefault(new Authenticator() {
        //    protected PasswordAuthentication getPasswordAuthentication() {
        //        return new PasswordAuthentication(proxy.getUser(), proxy.getPassword().toCharArray());
        //    }
        //});
        httpClient.getParams().setParameter("http.protocol.version", HttpVersion.HTTP_1_0);
        //httpClient.getParams().setParameter("http.socket.timeout", 1000);
        httpClient.getParams().setParameter("http.protocol.content-charset", "UTF-8");
        httpClient.getParams().setParameter("http.protocol.expect-continue", false);

        return true;
    }

    private static HttpClient httpClient = new DefaultHttpClient();


    @Override
    protected List<GeoAddress> doFindAddressByKeyword(GeoProviderSettings settings, String keyword, int i, List<Float> accuracies) throws GeoException {
        String region = settings.getGisParam(GisParameter.region.withProvider(getProviderName()));
        if (!setProperties())
            return new ArrayList<GeoAddress>();

        return new GeocodeRequest(httpClient, region, keyword, geoSettings.getLanguage(), accuracies).execute();
    }

    public GeoRoute findShortestRoute(GeographicCoordinates from, GeographicCoordinates to) throws GeoException {
        if (!setProperties())
            return null;

        return new RouteRequest(httpClient, from, to, RouteRequest.RoutePreference.SHORTEST).execute();
    }

    public GeoRoute findFastestRoute(GeographicCoordinates from, GeographicCoordinates to) throws GeoException {
        if (!setProperties())
            return null;

        return new RouteRequest(httpClient, from, to, RouteRequest.RoutePreference.FASTEST).execute();
    }

    public GeoRoute getRoute(GeographicCoordinates from, GeographicCoordinates to, VehicleType vehicleType) throws GeoException {
        throw new UnsupportedOperationException("Method getRoute of class com.magenta.maxoptra.geocoding.decarta.GeocoderDecartaImpl is unimplemented!");
    }

    @Override
    public List<GeoLocationRecord> getRoutePoints(GeoProviderSettings settings, GeographicCoordinates from, GeographicCoordinates to) throws GeoException {
        throw new UnsupportedOperationException();
    }

    public void setClient(GeoInformationClient client) {
        this.client = client;
    }

    @Override
    public List<GeoAddress> doForwardGeocoding(GeoProviderSettings settings, GeoAddress address, int resultAddressLimit)
            throws GeoException {
        throw new UnsupportedOperationException("");
    }

    @Override
    protected GisParameters getProviderParams() {
        return client.getProperties();
    }

    @Override
    public GeoRoute getRoute(List<GeographicCoordinates> coordinates, VehicleType vehicleType) throws GeoException {
        throw new UnsupportedOperationException("getRoute by list of coordinates is not supported for GeocoderDecarta");
    }

    @Override
    public List<GeoRoute> getRoute(GeographicCoordinates from, List<? extends GeographicCoordinates> to, VehicleType vehicleType, int timeout) throws GeoException {
        throw new UnsupportedOperationException("getRoute from coordinate to list of coordinates is not supported for GeocoderDecarta");
    }

    public synchronized List<GeoLocationRecord> getRoutePoints(GeoProviderSettings settings, List<GeographicCoordinates> coordinates) throws GeoException {
        throw new UnsupportedOperationException("getRoutePoints by list of coordinates is not supported for GeocoderDecarta");
    }

    public GeoAddress findCountry(String country, String keyword) throws GeoException {
        throw new UnsupportedOperationException("IGeocoding.findCountry is not implemented for GeocoderDecarta");
    }

    @Override
    public List<GeoRoute> getRoutes(GeographicCoordinates from, List<? extends GeographicCoordinates> to, VehicleType vehicleType, int timeout) throws GeoException {
        throw new UnsupportedOperationException("getRoutes from coordinate to list of coordinates is not supported for GeocoderDecarta");
    }

    @Override
    public List<GeoRoute> getCoordinateToCoordinateRoutes(List<GeographicCoordinates> coordinates) throws ServerUnavailableException {
        throw new UnsupportedOperationException("getCoordinateToCoordinateRoutes from coordinate to list of coordinates is not supported for " + getProviderName());
    }

    @Override
    public List<GeoRoute> getRoutes(GeographicCoordinates from, List<? extends GeographicCoordinates> to, int timeout) throws GeoException, ServerUnavailableException {
        return getRoutes(from, to, timeout);
    }

    @Override
    public List<GeoRoute> getRoute(GeographicCoordinates from, List<? extends GeographicCoordinates> to, int timeout) throws GeoException {
        return getRoute(from, to, null, timeout);
    }

    @Override
    public GeoRoute getRoute(List<GeographicCoordinates> coordinates) throws GeoException {
        return getRoute(coordinates, null);
    }

    @Override
    public synchronized GeoRoute getRoute(GeographicCoordinates from, GeographicCoordinates to) throws GeoException, ServerUnavailableException {
        return getRoute(from, to, null);
    }

}
